package com.ssy.lingxi.order.serviceimpl.mobile;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.ObjectUtil;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.ssy.lingxi.common.constant.order.OrderTypeEnum;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.model.vo.PageVO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.member.merchant.api.feign.inner.MemberAbilitySalesFeign;
import com.ssy.lingxi.member.merchant.api.feign.inner.MobileMemberSalesFeign;
import com.ssy.lingxi.member.merchant.api.model.vo.inner.request.MemberSalesFindUserIdVO;
import com.ssy.lingxi.member.merchant.api.model.vo.inner.response.MemberSalesFeignPageQueryVO;
import com.ssy.lingxi.order.entity.QOrderDO;
import com.ssy.lingxi.order.entity.QOrderPaymentDO;
import com.ssy.lingxi.order.entity.QOrderProductDO;
import com.ssy.lingxi.order.model.constant.OrderOuterStatusEnum;
import com.ssy.lingxi.order.model.vo.basic.response.OrderAmountVO;
import com.ssy.lingxi.order.model.vo.mobile.request.MobileWechatAppletBindMemberOrderCountVO;
import com.ssy.lingxi.order.model.vo.mobile.response.MobileMemberSalesOrderCountQueryVO;
import com.ssy.lingxi.order.model.vo.mobile.response.MobileAchievementCountQueryVO;
import com.ssy.lingxi.order.model.vo.mobile.response.MobileMemberSalesCountQueryVO;
import com.ssy.lingxi.order.model.vo.mobile.response.MobileWechatAppletAchievementCountVO;
import com.ssy.lingxi.order.service.base.IBaseCacheService;
import com.ssy.lingxi.order.service.mobile.IMobileMemberSalesCountService;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 业务员小程序-业绩统计相关接口
 *
 * @author 郑小翠
 * @version 2.02.18
 * @date 2022-03-24
 */
@Service
public class MobileMemberSalesCountServiceImpl implements IMobileMemberSalesCountService {

    @Resource
    MobileMemberSalesFeign mobileMemberSalesFeign;

    @Resource
    MemberAbilitySalesFeign memberAbilitySalesFeign;

    @Resource
    IBaseCacheService baseCacheService;

    @Resource
    JPAQueryFactory jpaQueryFactory;


    /**
     * 根据业务员Id查询业绩并进行统计返回
     *
     * @param headers 头部信息
     * @return 返回符合条件的业务员
     */
    @Override
    public Wrapper<MobileMemberSalesOrderCountQueryVO> orderCountWechatApplet(HttpHeaders headers) {
        // 首页业绩统计主要是统计全部已经完成的金额和本月的金额
        // 步骤1：计算该业务员的全部订单总金额。根据业务员Id，订单类型，用户角色为消费者且订单状态为已完成这四个查询条件，找到所有订单，订单完成总金额 = 已经支付 - 已经退款
        // 步骤2：计算全部下级会员数。根据登录的会员Id，角色Id和业务员Id 找到该业务员下面绑定的所有下级会员，将下级会员数统计到memberAllQuantity这个字段里
        // 步骤3：计算本月的应付金额和已付金额。根据当前系统的时间，得到当月的开始时间1号0点和结束时间最后一天的最后一刻
        // 然后根据业务员Id，订单类型，角色为消费者，开始时间和结束时间对应的订单提交时间，找到本月符合条件的订单进行统计
        // 步骤4：计算本月的完成金额。本月订单状态为已完成，本月完成金额 = 订单已付金额 - 订单退款金额
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromMobile(headers);
        Long userId = loginUser.getUserId();
        MobileMemberSalesOrderCountQueryVO queryVO = new MobileMemberSalesOrderCountQueryVO();
        // 本月的开始时间和结束时间
        LocalDate currentMonth = LocalDate.now();
        LocalDateTime startTime = LocalDateTime.of(currentMonth, LocalTime.MIN).with(TemporalAdjusters.firstDayOfMonth());
        LocalDateTime endTime = LocalDateTime.of(currentMonth, LocalTime.MAX).with(TemporalAdjusters.lastDayOfMonth());

        MemberSalesFindUserIdVO userIdVO = new MemberSalesFindUserIdVO(loginUser.getMemberId(), loginUser.getMemberRoleId(), userId, null, null, null, null);
        Wrapper<PageData<MemberSalesFeignPageQueryVO>> userWrapper = mobileMemberSalesFeign.getSalesList(userIdVO);
        if (ObjectUtil.isNull(userWrapper.getData())) {
            return Wrapper.success(queryVO);
        }
        // 得到全部会员
        List<MemberSalesFeignPageQueryVO> memberList = userWrapper.getData().getData();
        if (CollectionUtil.isEmpty(memberList)) {
            return Wrapper.success(queryVO);
        }
        // 得到角色为消费者的Ids
        Wrapper<List<Long>> roleIdsWrapper = memberAbilitySalesFeign.roleIds();
        List<Long> roles = roleIdsWrapper.getData();
        // 订单类型
        List<Integer> orderTypes = orderTypes();

        // 步骤1：计算该业务员的全部订单总金额，订单完成总金额 = 已经支付 - 已经退款
        PageData<MobileMemberSalesCountQueryVO> memberCountDataVO = indexOrderCount(userId, orderTypes, roles, null, null);
        if (CollectionUtil.isEmpty(memberCountDataVO.getData())) {
            queryVO.setOrderFinishAllAmount(new BigDecimal(0));
        }
        List<MobileMemberSalesCountQueryVO> orderCountVO = memberCountDataVO.getData();
        // 订单完成全部已支付金额
        BigDecimal reduce = orderCountVO.stream().map(MobileMemberSalesCountQueryVO::getAmountPaid).filter(ObjectUtil::isNotNull).reduce(new BigDecimal(0), BigDecimal::add);
        // 订单完成全部已退款金额
        BigDecimal returnAmountAll = orderCountVO.stream().map(MobileMemberSalesCountQueryVO::getRefundAmount).filter(ObjectUtil::isNotNull).reduce(new BigDecimal(0), BigDecimal::add);
        // 订单完成全部完成金额 = 全部已支付金额 - 全部已退款金额
        queryVO.setOrderFinishAllAmount(reduce.subtract(returnAmountAll));

        // 步骤2：计算全部下级会员数
        ArrayList<MemberSalesFeignPageQueryVO> userCount = memberList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(user -> user.getMemberId() + ";" + user.getMemberRoleId()))), ArrayList::new));
        queryVO.setMemberAllQuantity(userCount.size());

        // 步骤3：计算本月的应付金额和已付金额
        List<OrderAmountVO> orderAmountLists = paidAmount(userId, roles, orderTypes, startTime, endTime);
        ArrayList<OrderAmountVO> orderAmountList = orderAmountLists.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(OrderAmountVO::getOrderId))), ArrayList::new));
        // 本月的下单金额 -- 一提交订单就统计
        queryVO.setOrderAmount(orderAmountList.stream().map(OrderAmountVO::getTotalAmount).filter(ObjectUtil::isNotNull).reduce(new BigDecimal(0), BigDecimal::add));
        // 本月的已收款金额 -- 一确认钱到账就统计
        BigDecimal amountPaid = orderAmountList.stream().map(OrderAmountVO::getPaidAmount).filter(ObjectUtil::isNotNull).reduce(new BigDecimal(0), BigDecimal::add);
        queryVO.setOrderPaidAmount(amountPaid);

        // 步骤4：计算本月的完成金额，订单状态为已完成
        PageData<MobileMemberSalesCountQueryVO> queryPageData = indexOrderCount(userId, orderTypes, roles, startTime, endTime);
        if (CollectionUtils.isEmpty(queryPageData.getData())) {
            queryVO.setOrderFinishAmount(new BigDecimal(0));
        } else {
            // 本月的完成金额  -- 收款金额-退款金额
            BigDecimal amountPaidFinish = queryPageData.getData().stream().map(MobileMemberSalesCountQueryVO::getAmountPaid).filter(ObjectUtil::isNotNull).reduce(new BigDecimal(0), BigDecimal::add);
            BigDecimal returnAmountFinish = queryPageData.getData().stream().map(MobileMemberSalesCountQueryVO::getRefundAmount).filter(ObjectUtil::isNotNull).reduce(new BigDecimal(0), BigDecimal::add);
            queryVO.setOrderFinishAmount(amountPaidFinish.subtract(returnAmountFinish));
        }
        return Wrapper.success(queryVO);
    }

    /**
     * 业务员小程序 - 查看绑定会员订单统计
     *
     * @param headers      头部信息
     * @param orderCountVO 搜索时间
     * @return 返回会员订单统计列表
     */
    @Override
    public Wrapper<PageData<MobileMemberSalesCountQueryVO>> bindMemberOrderCountWechatApplet(HttpHeaders headers, MobileWechatAppletBindMemberOrderCountVO orderCountVO) {
        // 根据登录的业务员Id找到所有该业务员的下级会员，再根据业务员Id，下级会员Id和角色Id,找到符合条件的订单并进行统计
        // 步骤1：根据业务员Id找到所有下级会员
        // 步骤2：根据下级会员信息找到所有订单
        // 步骤3：对得到的订单进行统计。其中订单状态为已取消的金额不计算，有总金额 已付金额 退款金额。下单数量要把已经取消的订单也算进去
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromMobile(headers);

        // 拼接时间，得到选择的月开始时间和月结束时间
        String currentTime = orderCountVO.getCountTime().concat("-01");
        LocalDate selectTime = LocalDate.parse(currentTime, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        LocalDateTime startTime = LocalDateTime.of(selectTime, LocalTime.MIN).with(TemporalAdjusters.firstDayOfMonth());
        LocalDateTime endTime = LocalDateTime.of(selectTime, LocalTime.MAX).with(TemporalAdjusters.lastDayOfMonth());

        // 步骤1：根据业务员Id找到所有下级会员，对下级会员进行分页查询
        MemberSalesFindUserIdVO userIdVO = new MemberSalesFindUserIdVO(loginUser.getMemberId(), loginUser.getMemberRoleId(), loginUser.getUserId(), orderCountVO.getMemberName(), endTime, orderCountVO.getCurrent(), orderCountVO.getPageSize());
        Wrapper<PageData<MemberSalesFeignPageQueryVO>> userWrapper = mobileMemberSalesFeign.getSalesList(userIdVO);
        // 不分页数据
        userIdVO.setCurrent(null);
        userIdVO.setPageSize(null);
        Wrapper<PageData<MemberSalesFeignPageQueryVO>> userWrappers = mobileMemberSalesFeign.getSalesList(userIdVO);
        if (ObjectUtil.isNull(userWrapper.getData()) || CollectionUtils.isEmpty(userWrapper.getData().getData())) {
            return Wrapper.success(new PageData<>(0L, new ArrayList<>()));
        }
        List<MemberSalesFeignPageQueryVO> memberList = userWrapper.getData().getData();

        // 订单类型
        List<Integer> orderTypes = orderTypes();

        // 步骤2：根据下级会员信息找到所有订单
        PageData<MobileMemberSalesCountQueryVO> queryPageDataVO = orderCount(memberList, loginUser.getUserId(), orderTypes, startTime, endTime, orderCountVO.getMemberName(), 0);
        PageData<MobileMemberSalesCountQueryVO> orderQuantityVO = orderCount(memberList, loginUser.getUserId(), orderTypes, startTime, endTime, orderCountVO.getMemberName(), null);

        // 步骤3：对得到的订单进行统计，并对没有订单的下级会员赋初始值
        List<MobileMemberSalesCountQueryVO> result = memberList.stream().map(member -> {
            MobileMemberSalesCountQueryVO viewVO = new MobileMemberSalesCountQueryVO();
            // 符合条件的绑定会员订单统计--计算取消订单的数据
            List<MobileMemberSalesCountQueryVO> countQueryVO = queryPageDataVO.getData().stream().filter(queryVO -> member.getMemberId().equals(queryVO.getMemberId()) && member.getMemberRoleId().equals(queryVO.getMemberRoleId())).collect(Collectors.toList());
            // 计算不取消的订单数量
            List<MobileMemberSalesCountQueryVO> countQuery = orderQuantityVO.getData().stream().filter(queryVO -> member.getMemberId().equals(queryVO.getMemberId()) && member.getMemberRoleId().equals(queryVO.getMemberRoleId())).collect(Collectors.toList());
            // 会员Id
            viewVO.setMemberId(member.getMemberId());
            // 会员名字
            viewVO.setMemberName(member.getMemberName());
            // 会员角色Id
            viewVO.setMemberRoleId(member.getMemberRoleId());
            // 会员角色名称
            viewVO.setRoleName(member.getRoleName());

            if (!CollectionUtils.isEmpty(countQueryVO)) {
                // 总下单数
                ArrayList<MobileMemberSalesCountQueryVO> orderCount = countQuery.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(MobileMemberSalesCountQueryVO::getOrderCount))), ArrayList::new));
                viewVO.setOrderCount((long) orderCount.size());
                // 总金额
                viewVO.setAmountPayable(countQueryVO.stream().map(MobileMemberSalesCountQueryVO::getAmountPayable).reduce(new BigDecimal(0), BigDecimal::add));
                // 已付金额
                viewVO.setAmountPaid(countQueryVO.stream().map(MobileMemberSalesCountQueryVO::getAmountPaid).reduce(new BigDecimal(0), BigDecimal::add));
                // 退款金额
                viewVO.setRefundAmount(countQueryVO.stream().map(MobileMemberSalesCountQueryVO::getRefundAmount).filter(ObjectUtil::isNotNull).reduce(new BigDecimal(0), BigDecimal::add));
                // 支付次数
                viewVO.setPayCount(0);
            } else {
                // 对没有订单数据的会员赋初始值
                viewVO.setOrderCount(0L);
                viewVO.setAmountPayable(new BigDecimal(0));
                viewVO.setAmountPaid(new BigDecimal(0));
                viewVO.setRefundAmount(new BigDecimal(0));
                viewVO.setPayCount(0);
            }
            return viewVO;
        }).sorted(Comparator.comparing(MobileMemberSalesCountQueryVO::getMemberId).reversed()).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(userWrappers.getData().getTotalCount(), result));
    }

    /**
     * 查看业务员个人业绩
     *
     * @param headers 头部信息
     */
    @Override
    public Wrapper<PageData<MobileAchievementCountQueryVO>> personalAchievement(HttpHeaders headers, PageVO pageVO) {
        // 根据业务员Id对订单数据按月份分组统计
        // 步骤1：按月分页，计算开始月份和结束月份。根据分页信息判断是否从当前月开始往后统计的，如果不是的话，需要把开始月份计算出来
        // 步骤2：根据月份和查询条件找到所有订单，对订单数据按月进行分组统计
        // 步骤3：计算总条数。当前月份的时间-订单开始月份
        // 步骤4：填充没有业绩的月份，并为没有业绩的月份赋初始值0
        // 步骤5：将初始化的月份添加到有业绩的月份里，并一起返回给前端
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromMobile(headers);

        // 步骤1：按月分页，计算开始月份和结束月份
        // 月份开始时间
        LocalDate startMonth;
        if (pageVO.getCurrent() == 1) {
            startMonth = LocalDate.now();
        } else {
            int month = (pageVO.getCurrent() - 1) * pageVO.getPageSize();
            startMonth = LocalDate.now().minusMonths(month);
        }
        LocalDateTime startTime = LocalDateTime.of(startMonth, LocalTime.MAX).with(TemporalAdjusters.lastDayOfMonth());
        // 月份结束时间
        int endMonthInt = pageVO.getCurrent() * pageVO.getPageSize() - 1;
        LocalDate endMonth = LocalDate.now().minusMonths(endMonthInt);
        LocalDateTime endTime = LocalDateTime.of(endMonth, LocalTime.MIN).with(TemporalAdjusters.firstDayOfMonth());

        // 得到角色为消费者的Ids
        Wrapper<List<Long>> roleIdsWrapper = memberAbilitySalesFeign.roleIds();
        List<Long> roleIds = roleIdsWrapper.getData();

        // 根据业务员Id找到下级会员信息
        MemberSalesFindUserIdVO userIdVO = new MemberSalesFindUserIdVO(loginUser.getMemberId(), loginUser.getMemberRoleId(), loginUser.getUserId(), null, null, null, null);
        Wrapper<PageData<MemberSalesFeignPageQueryVO>> userWrapper = mobileMemberSalesFeign.getSalesList(userIdVO);

        PageData<MobileAchievementCountQueryVO> result = updatePersonalAchievementNow(userWrapper, roleIds, loginUser.getUserId(), startTime, endTime, pageVO.getCurrent());

        return Wrapper.success(result);
    }

    /**
     * 首页统计
     */
    private PageData<MobileMemberSalesCountQueryVO> indexOrderCount(Long userId, List<Integer> orderTypes, List<Long> roleIds, LocalDateTime startTime, LocalDateTime endTime) {
        QOrderDO qOrder = QOrderDO.orderDO;
        QOrderProductDO qOrderProduct = QOrderProductDO.orderProductDO;
        JPAQuery<MobileMemberSalesCountQueryVO> query = jpaQueryFactory.select(Projections.constructor(MobileMemberSalesCountQueryVO.class, qOrder.id, qOrder.totalAmount, qOrder.paidAmount, qOrderProduct.refReturnAmount.sum()))
                .from(qOrder)
                .leftJoin(qOrderProduct).on(qOrderProduct.order.id.eq(qOrder.id))
                .where(qOrder.vendorUserId.eq(userId))
                .where(qOrder.orderType.in(orderTypes))
                .where(qOrder.buyerRoleId.in(roleIds))
                .where(qOrder.outerStatus.eq(OrderOuterStatusEnum.ACCOMPLISHED.getCode()))
                .groupBy(qOrder.id, qOrder.totalAmount, qOrder.paidAmount);

        if (ObjectUtil.isNotNull(startTime) && ObjectUtil.isNotNull(endTime)) {
            query.where(qOrder.finishTime.after(startTime).and(qOrder.finishTime.before(endTime)));
        }
        //总数
        long totalCount = query.fetch().size();
        List<MobileMemberSalesCountQueryVO> result = query.fetch();
        return new PageData<>(totalCount, result);
    }

    /**
     * 绑定会员订单统计
     */
    private PageData<MobileMemberSalesCountQueryVO> orderCount(List<MemberSalesFeignPageQueryVO> memberList, Long userId, List<Integer> orderTypes, LocalDateTime startTime, LocalDateTime endTime, String memberName, Integer orderStatus) {
        QOrderDO qOrder = QOrderDO.orderDO;
        QOrderProductDO qOrderProduct = QOrderProductDO.orderProductDO;
        //拼接采购商会员Id、角色Id
        com.querydsl.core.types.Predicate[] predicates = memberList.stream().map(member -> qOrder.buyerMemberId.eq(member.getMemberId()).and(qOrder.buyerRoleId.eq(member.getMemberRoleId()))).toArray(com.querydsl.core.types.Predicate[]::new);

        JPAQuery<MobileMemberSalesCountQueryVO> query = jpaQueryFactory.select(Projections.constructor(MobileMemberSalesCountQueryVO.class, qOrder.buyerMemberId, qOrder.buyerMemberName, qOrder.buyerRoleId, qOrder.id, qOrder.totalAmount, qOrder.paidAmount, qOrderProduct.refReturnAmount.sum()))
                .from(qOrder)
                .leftJoin(qOrderProduct).on(qOrderProduct.order.id.eq(qOrder.id))
                .where(ExpressionUtils.anyOf(predicates))
                .where(qOrder.vendorUserId.eq(userId))
                .where(qOrder.orderType.in(orderTypes))
                .groupBy(qOrder.buyerMemberId, qOrder.buyerMemberName, qOrder.buyerRoleId, qOrder.id, qOrder.totalAmount, qOrder.paidAmount);

        if (ObjectUtil.isNotNull(orderStatus)) {
            query.where(qOrder.outerStatus.notIn(101));
        }
        if (ObjectUtil.isNotNull(startTime) && ObjectUtil.isNotNull(endTime)) {
            query.where(qOrder.submitTime.after(startTime).and(qOrder.submitTime.before(endTime)));
        }

        if (StringUtils.hasLength(memberName)) {
            query.where(qOrder.buyerMemberName.like("%" + memberName.trim() + "%"));
        }
        //总数
        long totalCount = query.fetch().size();

        List<MobileMemberSalesCountQueryVO> result = query.orderBy(qOrder.buyerMemberId.asc()).fetch();

        return new PageData<>(totalCount, result);
    }

    /**
     * 业务员个人业绩更新
     * @param userWrapper 下级会员信息
     * @param roleIds 角色类型
     * @param userId 业务员Id
     * @param startTime 开始月份
     * @param endTime 结束月份
     * @param current 当前页
     * @return 根据月份进行分组统计每月的业绩
     */
    private PageData<MobileAchievementCountQueryVO> updatePersonalAchievementNow(Wrapper<PageData<MemberSalesFeignPageQueryVO>> userWrapper, List<Long> roleIds, Long userId, LocalDateTime startTime, LocalDateTime endTime, Integer current) {
        // 订单类型
        List<Integer> orderTypes = orderTypes();

        // 步骤2：根据月份和查询条件找到所有订单，对订单数据按月进行分组统计
        // 不包含订单状态为取消的--下单会员数，应付金额，已付金额，退款金额
        List<MobileWechatAppletAchievementCountVO> resultCount = getPersonalAchievement(orderTypes, roleIds, userId, startTime, endTime, 0);
        // 包含订单状态为取消的--订单数量、商品数量、品类数量
        List<MobileWechatAppletAchievementCountVO> resultCounts = getPersonalAchievement(orderTypes, roleIds, userId, startTime, endTime, null);
        // 总条数
        List<MobileWechatAppletAchievementCountVO> totalCounts = getPersonalAchievement(orderTypes, roleIds, userId, null, null, null);
        long totalCount;
        // 总条数
        Optional<Integer> startOrderMonth = totalCounts.stream().map(MobileWechatAppletAchievementCountVO::getYear).filter(ObjectUtil::isNotNull).distinct().sorted().findFirst();
        if (startOrderMonth.isPresent()) {
            // 订单开始时间
            int startOrderMonthInteger = startOrderMonth.get();
            LocalDate orderStartMonth = LocalDate.of(startOrderMonthInteger / 100, startOrderMonthInteger % 100, 1);
            // 当前时间
            LocalDate currentYear = LocalDate.now();
           // 相差月份
            totalCount = orderStartMonth.until(currentYear, ChronoUnit.MONTHS) + 1;
        } else {
            List<MobileAchievementCountQueryVO> resultVO =new ArrayList<>();
            MobileAchievementCountQueryVO vo = new MobileAchievementCountQueryVO();
            // 月统计时间
            vo.setMonthStatistical(LocalDate.now().withDayOfMonth(1));
            vo.setMemberCount(0L);
            vo.setCategoryCount(0L);
            vo.setOrderCount(0L);
            vo.setCommodityCount(0L);
            vo.setAmountPaid(BigDecimal.ZERO);
            vo.setAmountPayable(BigDecimal.ZERO);
            vo.setRefundAmount(BigDecimal.ZERO);
            vo.setMemberUserId(userId);
            resultVO.add(vo);
            totalCount = 1L;
            return new PageData<>(totalCount, resultVO);
        }
        // 对查询出来的订单根据月份进行分组统计，转成前端接收的VO
        String day = "01";
        List<MobileAchievementCountQueryVO> queryVO = resultCounts.stream().filter(ObjectUtil::isNotNull).collect(Collectors.groupingBy(MobileWechatAppletAchievementCountVO::getYear)).entrySet().stream().map(entry -> {
            Integer key = entry.getKey();
            // 这个月--不包含订单状态为取消的--下单会员数，应付金额，已付金额，退款金额
            List<MobileWechatAppletAchievementCountVO> values = resultCount.stream().filter(c -> key.equals(c.getYear())).filter(ObjectUtil::isNotNull).collect(Collectors.toList()); //
            // 月统计时间
            LocalDate month = LocalDate.parse(key.toString().substring(0, 4) + "-" + key.toString().substring(4) + "-" + day, DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN));

            // 构建返回的VO
            MobileAchievementCountQueryVO vo = new MobileAchievementCountQueryVO();
            vo.setMonthStatistical(month);
            // 这个月--包含订单状态为取消的--订单次数、商品数量、品类数量
            List<MobileWechatAppletAchievementCountVO> numbers = entry.getValue();
            vo.setMemberUserId(userId);
            // 订单次数
            vo.setOrderCount(numbers.stream().map(MobileWechatAppletAchievementCountVO::getOrderId).filter(ObjectUtil::isNotNull).distinct().count());
            // 订单商品种类数量统计
            vo.setCommodityCount(numbers.stream().map(MobileWechatAppletAchievementCountVO::getCommodityId).filter(ObjectUtil::isNotNull).distinct().count());
            // 商品品类种类数量
            List<String> categoryCount = numbers.stream().map(MobileWechatAppletAchievementCountVO::getCategory).filter(ObjectUtil::isNotNull).distinct().collect(Collectors.toList());
            vo.setCategoryCount((long) categoryCount.size());
            if (CollectionUtils.isEmpty(resultCount)) {
                vo.setMemberCount(0L);
                vo.setAmountPaid(BigDecimal.ZERO);
                vo.setAmountPayable(BigDecimal.ZERO);
                vo.setRefundAmount(BigDecimal.ZERO);
            } else {
                // 下单会员数
                ArrayList<MobileWechatAppletAchievementCountVO> memberCount = values.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(order -> order.getBuyMemberId() + ";" + order.getBuyMemberRoleId()))), ArrayList::new));
                if (ObjectUtil.isNull(userWrapper.getData()) || ObjectUtil.isNull(userWrapper.getData().getData())) {
                    vo.setMemberCount(0L);
                } else {
                    List<MemberSalesFeignPageQueryVO> memberUserList = userWrapper.getData().getData();
                    List<MemberSalesFeignPageQueryVO> count = memberUserList.stream().filter(v1 -> memberCount.stream().anyMatch(v2 -> v1.getMemberId().equals(v2.getBuyMemberId()) && v1.getMemberRoleId().equals(v2.getBuyMemberRoleId()))).collect(Collectors.toList());
                    vo.setMemberCount((long) count.size());
                }
                ArrayList<MobileWechatAppletAchievementCountVO> amountPayableList = values.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(MobileWechatAppletAchievementCountVO::getOrderId))), ArrayList::new));

                // 已经支付的金额
                vo.setAmountPaid(amountPayableList.stream().map(MobileWechatAppletAchievementCountVO::getAmountPaid).filter(ObjectUtil::isNotNull).reduce(new BigDecimal(0), BigDecimal::add));
                // 订单应该支付的金额
                vo.setAmountPayable(amountPayableList.stream().map(MobileWechatAppletAchievementCountVO::getAmountPayable).filter(ObjectUtil::isNotNull).reduce(new BigDecimal(0), BigDecimal::add));
                // 退款金额
                vo.setRefundAmount(values.stream().map(MobileWechatAppletAchievementCountVO::getRefundAmount).filter(ObjectUtil::isNotNull).reduce(new BigDecimal(0), BigDecimal::add));
            }
            return vo;
        }).collect(Collectors.toList());

        // 步骤3：填充没有业绩的月份，并为没有业绩的月份赋初始值0
        List<Integer> months = resultCounts.stream().map(MobileWechatAppletAchievementCountVO::getYear).filter(ObjectUtil::isNotNull).distinct().sorted().collect(Collectors.toList());
        List<MobileAchievementCountQueryVO> initializationVO = monthsHandle(months, startTime.toLocalDate(), endTime.toLocalDate(), startOrderMonth.get(), userId);

        // 步骤4：将初始化的月份添加到有业绩的月份里，并一起返回给前端
        if(!CollectionUtils.isEmpty(initializationVO)){
            // 当初始化VO不为空时，加入queryVO里面
            queryVO.addAll(initializationVO);
        }

        List<MobileAchievementCountQueryVO> result = queryVO.stream().sorted(Comparator.comparing(MobileAchievementCountQueryVO::getMonthStatistical).reversed()).collect(Collectors.toList());
        return new PageData<>(totalCount, result);
    }

    /**
     * 业务员个人业绩数据查询
     */
    private List<MobileWechatAppletAchievementCountVO> getPersonalAchievement(List<Integer> orderTypes, List<Long> roleIds, Long userId, LocalDateTime startTime, LocalDateTime endTime, Integer orderStatus) {
        QOrderDO qOrder = QOrderDO.orderDO;
        QOrderProductDO qOrderProduct = QOrderProductDO.orderProductDO;
        JPAQuery<MobileWechatAppletAchievementCountVO> query =
                jpaQueryFactory.select(Projections.constructor(MobileWechatAppletAchievementCountVO.class, qOrder.submitTime.yearMonth().as("yearMonth"), qOrder.vendorUserId, qOrder.buyerMemberId, qOrder.buyerRoleId, qOrder.id, qOrderProduct.productId, qOrderProduct.category, qOrder.totalAmount, qOrder.paidAmount, qOrderProduct.refReturnAmount))
                        .from(qOrderProduct)
                        .leftJoin(qOrder).on(qOrderProduct.order.id.eq(qOrder.id))
                        .where(qOrder.orderType.in(orderTypes))
                        .where(qOrder.buyerRoleId.in(roleIds))
                        .groupBy(qOrder.vendorUserId, qOrder.buyerMemberId, qOrder.buyerRoleId, qOrder.id, qOrderProduct.productId, qOrderProduct.category, qOrder.paidAmount, qOrderProduct.refReturnAmount, qOrderProduct.refPrice, qOrderProduct.quantity);

        if (ObjectUtil.isNotNull(startTime) && ObjectUtil.isNotNull(endTime)) {
            query.where(qOrder.submitTime.after(endTime).and(qOrder.submitTime.before(startTime)));
        }
        if (ObjectUtil.isNotNull(userId)) {
            query.where(qOrder.vendorUserId.eq(userId));
        }
        if (ObjectUtil.isNotNull(orderStatus)) {
            query.where(qOrder.outerStatus.notIn(101));
        }
        return query.orderBy(qOrder.submitTime.asc()).fetch();
    }

    /**
     * 业务员业绩统计-月份处理
     */
    private List<MobileAchievementCountQueryVO> monthsHandle(List<Integer> months, LocalDate startTime, LocalDate endTime, Integer totalCounts, Long userId) {
        // 月份处理这个方法，主要是把这十个月没有业绩的月份统计出来。
        // 当前分页查询的月份，有202204，202203，202202，202201，202112，202111，202110，202109，202108，202107
        // 比如有业绩的是202202，202110，202108,那么要把202204，202203，202201，202112，202111，202109，202107，这几个月业绩展示出来，并把初始业绩设置为0
        // 步骤：1.判断搜索的开始月份是否大于等于订单开始月份，如果大于判断查询出来的业绩月份有没有搜索开始月份，如果没有将搜索开始月份添加到notInMonths和months
        // 2.判断搜索的结束时间是否大于订单开始时间，如果大于判断查询出来的业绩月份有没有搜索结束月份,如果没有将搜索结束月份添加到notInMonths和months
        // 3.判断第一个月和第二个月是否连续，如果不连续将中间空缺的月份添加上，比如202202和202110，中间差了202201，202112,202111
        // 添加月份的逻辑：判断第一个月份的年和第二个月份的年，是否是同一年，是同一年直接月份加1就可以了
        // 不是同一年，就把第一个月份的年，取最后一个月，拿这一年的最后一个月（202112）减去第一个月份（202110），中间就是间隔的月份
        // 再把第二个月份的年，取第一个月，拿第二个月份（202202）的时间减去第一个月（202201），中间就是间隔的月数
        // 4.为没有业绩的月份赋初始值
        List<Integer> notInMonths = new ArrayList<>();
        // 步骤1：判断搜索的开始月份是否大于等于订单开始月份
        Integer selectStartMonth = Integer.valueOf(startTime.getYear() + startTime.toString().substring(5, 7));
        if (ObjectUtil.isNotNull(totalCounts) && selectStartMonth >= totalCounts) {
            if (!months.contains(selectStartMonth)) {
                notInMonths.add(selectStartMonth);
                months.add(selectStartMonth);
            }
        }
        // 步骤2：判断搜索的结束时间是否大于订单开始时间
        Integer selectEndMonth = Integer.valueOf(endTime.getYear() + endTime.toString().substring(5, 7));
        if (ObjectUtil.isNotNull(totalCounts) && selectEndMonth >= totalCounts) {
            if (!months.contains(selectEndMonth)) {
                notInMonths.add(selectEndMonth);
                months.add(selectEndMonth);
            }
        }
        List<Integer> allMonths = months.stream().filter(ObjectUtil::isNotNull).sorted().collect(Collectors.toList());
        // 步骤3：判断第一个月和第二个月是否连续
        for (int i = 0; i < allMonths.size() - 1; i++) {

            if (allMonths.get(i + 1) - allMonths.get(i) != 1) {
                // 第二个月(i + 1) - 第一个月(i） 不等于 1，说明月份不连续
                String lastYear = allMonths.get(i).toString().substring(0, 4);
                String thisYear = allMonths.get(i + 1).toString().substring(0, 4);
                if (lastYear.equals(thisYear)) {
                    // 两个月的年份相同
                    for (int j = 1; j < allMonths.get(i + 1) - allMonths.get(i); j++) {
                        // 月份加1
                        notInMonths.add(allMonths.get(i) + j);
                    }
                } else {
                    // 两个月的年份不同
                    // 第一个月份的最后一个月 - 第一个月份
                    int lastMonth = Integer.parseInt(lastYear + "12") - allMonths.get(i);
                    for (int j = 1; j <= lastMonth; j++) {
                        notInMonths.add(allMonths.get(i) + j);
                    }
                    // 第二个月份 - 第二个月份的最初一个月
                    int firstMonth = allMonths.get(i + 1) - Integer.parseInt(thisYear + "01");
                    for (int j = 1; j <= firstMonth; j++) {
                        notInMonths.add(allMonths.get(i + 1) - j);
                    }
                }
            }
        }
        // 步骤4：为没有业绩的月份赋初始值
        return notInMonths.stream().map(i -> {
            MobileAchievementCountQueryVO vo = new MobileAchievementCountQueryVO();
            // 月统计时间
            LocalDate month = LocalDate.parse(i.toString().substring(0, 4) + "-" + i.toString().substring(4) + "-" + "01", DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN));
            vo.setMonthStatistical(month);
            vo.setMemberCount(0L);
            vo.setCategoryCount(0L);
            vo.setOrderCount(0L);
            vo.setCommodityCount(0L);
            vo.setAmountPaid(BigDecimal.ZERO);
            vo.setAmountPayable(BigDecimal.ZERO);
            vo.setRefundAmount(BigDecimal.ZERO);
            vo.setMemberUserId(userId);
            return vo;
        }).collect(Collectors.toList());

    }

    /**
     * 根据业务员Id查找下单已付金额,应付金额，退款金额
     */
    private List<OrderAmountVO> paidAmount(Long userId, List<Long> roles, List<Integer> orderTypes, LocalDateTime startTime, LocalDateTime endTime) {
        QOrderDO qOrder = QOrderDO.orderDO;
        QOrderPaymentDO qOrderPaymentDO = QOrderPaymentDO.orderPaymentDO;
        QOrderProductDO qOrderProduct = QOrderProductDO.orderProductDO;
        JPAQuery<OrderAmountVO> query = jpaQueryFactory.select(Projections.constructor(OrderAmountVO.class, qOrder.id, qOrder.paidAmount, qOrder.totalAmount, qOrderProduct.refReturnAmount))
                .from(qOrder)
                .leftJoin(qOrderPaymentDO).on(qOrderPaymentDO.order.id.eq(qOrder.id))
                .leftJoin(qOrderProduct).on(qOrderProduct.order.id.eq(qOrder.id))
                .where(qOrder.orderType.in(orderTypes))
                .where(qOrder.vendorUserId.eq(userId))
                .where(qOrder.buyerRoleId.in(roles))
                .where(qOrder.outerStatus.notIn(101))
                .where(qOrder.submitTime.after(startTime).and(qOrder.submitTime.before(endTime)));

        return query.fetch();
    }


    /**
     * 订单类型
     */
    private ArrayList<Integer> orderTypes() {
        ArrayList<Integer> orderTypes = new ArrayList<>();
        orderTypes.add(OrderTypeEnum.INQUIRY_TO_PURCHASE.getCode());
        orderTypes.add(OrderTypeEnum.SPOT_PURCHASING.getCode());
        orderTypes.add(OrderTypeEnum.CHANNEL_STRAIGHT_MINING.getCode());
        orderTypes.add(OrderTypeEnum.CHANNEL_SPOT.getCode());
        return orderTypes;
    }
}
